/* Copyright 2001,2002,2003 NAH6
 * All Rights Reserved
 *
 * Parts Copyright DoD, Parts Copyright Starium
 *
 */
/* celp_flp.c */

/**********************/
/* CELP-CODEC (FLOAT) */
/* for Crypto-Phone   */
/* (C) 2002 by NAH6   */
/**********************/

/*------------*/
/*  INCLUDES  */
/*------------*/

#include <math.h>
#include <string.h>
#include <windows.h>

#include "celp_flp.h"

#include "celpfilt.h"
#include "analysis.h"
#include "codeparm.h"
#include "synth.h"

/*-------------*/
/*  VARIABLEN  */
/*-------------*/

static HINSTANCE codec_inst;

static LONG      codec_enc_free;
static int       codec_enc_frame;
static float     codec_enc_in[F_LEN];
static TX_PARAM  codec_enc_par;
static int      *codec_enc_out;

static LONG      codec_dec_free;
static int       codec_dec_frame;
static int       codec_dec_in[CELP_BITS];
static TX_PARAM  codec_dec_par;
static float     codec_dec_out[F_LEN];

/**************************************************************************
*                                                                         *
*	U.S. Department of Defense 			                  *
*	CELP Voice Coder                                                  *
*	Version 3.3c	                                                  *
*	1 June 1994	                                                  *
*                                                                         *
*	FOR OFFICIAL U.S. GOVERNMENT CONTRACT USE ONLY                    *
*									  *
*	The U.S. Government shall not be held liable for any damages      *
*	resulting from this code.  Further reproduction or distribution   *
*	of this code without prior written permission of the U.S.         *
*	Government is prohibited.  R224 may be contacted at:              *
*                                                                         *
*		DIRNSA, R224                                              *
*		9800 Savage Road                                          *
*		Fort Meade, MD 20755-6000 USA                             *
*                                                                         *
*==========================================================================
*
* REFERENCES
*
*	"Details to Assist in Implementation of Federal Standard 1016 CELP,"
*	Technical Information Bulletin 92-1, National Communications System,
*	(included as a Postscript file in CELP 3.3 Release)
*
*	"Federal Standard 1016," Telecommunications: Analog-to-Digital 
*	Conversion of Voice by 4800 bit/second Code Excited Linear
*	Predictive (CELP) Coding, National Communications System, distributed
*	by the General Services Administration:
*	    GSA Federal Supply Service Bureau
*	    Specification Section, Suite 8100
*	    470 E. L'Enfant Place, S.W.
*	    Washington, DC 20407
*	    (202)755-0325
*
*      Misc: The following articles describe the Federal-Standard-1016
*       4.8-kbps CELP coder (it's unnecessary to read more than one):
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The Federal Standard 1016 4800 bps CELP Voice Coder,"
*            Digital Signal Processing, Academic Press, 1991, Vol. 1, No.
*            3, p. 145-155.
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The DoD 4.8 kbps Standard (Proposed Federal Standard
*            1016)," in Advances in Speech Coding, ed. Atal, Cuperman and
*            Gersho, Kluwer Academic Publishers, 1991, Chapter 12, p.
*            121-133.
*          + Campbell, Joseph P. Jr., Thomas E. Tremain and Vanoy C.
*            Welch, "The Proposed Federal Standard 1016 4800 bps Voice
*            Coder: CELP," Speech Technology Magazine, April/May 1990, p.
*            58-64.
*	   
*
*
*******************************************************************************/

/*-------------*/
/*  WIN-ENTRY  */
/*-------------*/
                                                             /* Windows-Entry */
BOOL APIENTRY DllMain (HINSTANCE inst, DWORD reason, LPVOID reserved)
{
  if (reason == DLL_PROCESS_ATTACH)
  {
    codec_inst = inst;
    codec_enc_free = 1L;
    codec_dec_free = 1L;
  }
  else if (reason == DLL_PROCESS_DETACH)
  {
    codec_inst = NULL;
    codec_enc_free = 0L;
    codec_dec_free = 0L;
  }
  return TRUE;
}

/*-------------*/
/*  API-CALLS  */
/*-------------*/
                                                            /* Get Codec-Info */
EXPORT signed short CALLBACK codec_getinfo (struct codec_info *info)
{
  /* Check pointer */
  if (info == NULL)
    return E_CELP_FLP_NULLPTR;

  /* Write info */
  memset (info, 0, sizeof (struct codec_info));
  info->magic = K_CELP_FLP_MAGIC;
  info->ifver = K_CELP_FLP_IFVER;
  info->ifrev = K_CELP_FLP_IFREV;
  info->ifopt = K_CELP_FLP_IFOPT;
  info->enc_mask[0] = 0x40;
  info->dec_mask[0] = 0x40;
  return 0;
}

/*----*/
                                                              /* Get Revision */
EXPORT signed short CALLBACK codec_getrev (unsigned char codec)
{
  /* Check codec */
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Return revision */
  return K_CELP_FLP_REV0;
}

/*----------------------------------------------------------------------------*/

/*-----------*/
/*  ENCODER  */
/*-----------*/
                                                              /* Open Encoder */
EXPORT signed short CALLBACK codec_enc_create (unsigned char codec,            \
                                               void **state)
{
  LONG d;

  /* Check arguments */
  if (state == NULL)
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Check unused */
  d = InterlockedExchange (&codec_enc_free, 0L);
  if (!d)
    return E_CELP_FLP_INUSE;

  /* Init state-ptr */
  codec_enc_frame = 1;
  *state = &codec_enc_frame;

  /* Setup Analysis Filters */
  /* High Pass filter for input speech */
  InputHPFZ = makefilt (InputHPFCoefsZ, InputHPFOrder, InputHPFLength);
  InputHPFP = makefilt (InputHPFCoefsP, InputHPFOrder, InputHPFLength);
  /* Variable filters for determination of residual after LP analysis */
  LP_ResZ  = makefilt_dynamic (ORDER, RES_LEN);
  LP_ResP  = makefilt_dynamic (ORDER, RES_LEN);
  LP_ResP2 = makefilt_dynamic (ORDER, RES_LEN);
  /* Variable filters for determination of residual after Adaptive analysis */
  Adapt_ResZ  = makefilt_dynamic (ORDER, RES_LEN);
  Adapt_ResP  = makefilt_dynamic (ORDER, RES_LEN);
  Adapt_ResP2 = makefilt_dynamic (ORDER, RES_LEN);
  /* Variable filters for updating status of LP and Adaptive residual filters */
  Update_ResZ  = makefilt_dynamic (ORDER, RES_LEN);
  Update_ResP  = makefilt_dynamic (ORDER, RES_LEN);
  Update_ResP2 = makefilt_dynamic (ORDER, RES_LEN);
  return 0;
}

/*----*/
                                                             /* Close Encoder */
EXPORT signed short CALLBACK codec_enc_destroy (unsigned char codec,           \
                                                void *state)
{
  /* Check arguments */
  if (state == NULL)
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Free encoder */
  codec_enc_free = 1L;
  return 0;
}

/*====*/
                                                            /* Compress Audio */
EXPORT signed short CALLBACK codec_enc_data (unsigned char codec, void *state, \
                                             const signed short *audio,        \
                                             unsigned char *data,              \
                                             unsigned long *alen,              \
                                             unsigned long *dlen)
{
  unsigned char b,x,y;
  unsigned long a,d;
  const int *pi;
  float *pf;

  /* Check arguments */
  if ((state == NULL) || (audio == NULL) || (data == NULL) \
   || (alen == NULL)  || (dlen == NULL))
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Convert data */
  a = *alen;
  d = *dlen;
  while ((a >= F_LEN) && (d >= (CELP_BITS>>3)))
  {
    /* Integer to float */
    pf = codec_enc_in;
    for (x=0; x<F_LEN; x++)
      *pf++ = *audio++;
    /* Analyze input speech and calculate parameters */
    Analysis (codec_enc_in, &codec_enc_par, codec_enc_frame);
    /* Put analysis parameters into bitstream */
    EncodeParameters (codec_enc_par, CELP_BITS, NULL, FALSE, codec_enc_frame, \
                      &codec_enc_out);
    /* Pack bits */
    pi = codec_enc_out;
    for (x=0; x<(CELP_BITS>>3); x++)
    {
      b = 0x00;
      for (y=0; y<8; y++)
      {
        b >>= 1;
        if (*pi++)
          b |= 0x80;
      }
      *data++ = b;
    }
    codec_enc_frame++;
    a -= F_LEN;
    d -= (CELP_BITS>>3);
  }
  *alen = *alen - a;
  *dlen = *dlen - d;
  return 0;
}

/*----------------------------------------------------------------------------*/

/*-----------*/
/*  DECODER  */
/*-----------*/
                                                              /* Open Decoder */
EXPORT signed short CALLBACK codec_dec_create (unsigned char codec,            \
                                               void **state)
{
  LONG d;

  /* Check arguments */
  if (state == NULL)
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Check unused */
  d = InterlockedExchange (&codec_dec_free, 0L);
  if (!d)
    return E_CELP_FLP_INUSE;

  /* Init state-ptr */
  codec_dec_frame = 1;
  *state = &codec_dec_frame;

  /* Setup Synthesis Filters */
  /* Variable filter for LP synthesis */
  LP_Filt = makefilt_dynamic (ORDER, SF_LEN);
  /* Variable filter for post filter */
  PostZ  = makefilt_dynamic (ORDER, SF_LEN);
  PostZ2 = makefilt_dynamic (1, SF_LEN);
  PostP  = makefilt_dynamic (ORDER, SF_LEN);
  /* High Pass filter for output speech */
  OutputHPFZ = makefilt (InputHPFCoefsZ, 2, SF_LEN);
  OutputHPFP = makefilt (InputHPFCoefsP, 2, SF_LEN);
  return 0;
}

/*----*/
                                                             /* Close Decoder */
EXPORT signed short CALLBACK codec_dec_destroy (unsigned char codec,           \
                                                void *state)
{
  /* Check arguments */
  if (state == NULL)
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Free decoder */
  codec_dec_free = 1L;
  return 0;
}

/*====*/
                                                              /* Expand Audio */
EXPORT signed short CALLBACK codec_dec_data (unsigned char codec, void *state, \
                                             const unsigned char *data,        \
                                             signed short *audio,              \
                                             unsigned long *dlen,              \
                                             unsigned long *alen)
{
  unsigned char b,x,y;
  unsigned long a,d;
  const float *pf;
  float f;
  int *pi;

  /* Check arguments */
  if ((state == NULL) || (data == NULL) || (audio == NULL) \
   || (dlen == NULL)  || (alen == NULL))
    return E_CELP_FLP_NULLPTR;
  if (codec != K_CELP_FLP_ID0)
    return E_CELP_FLP_CODEC;

  /* Convert data */
  d = *dlen;
  a = *alen;
  while ((d >= (CELP_BITS>>3)) && (a >= F_LEN))
  {
    /* Unpack bits */
    pi = codec_dec_in;
    for (x=0; x<(CELP_BITS>>3); x++)
    {
      b = *data++;
      for (y=0; y<8; y++)
      {
        *pi++ = b & 0x01;
        b >>= 1;
      }
    }
    /* Get parameters from bitstream */
    DecodeParameters (codec_dec_in, CELP_BITS, NULL, FALSE, codec_dec_frame, \
                      TRUE, &codec_dec_par);
    /* Synthesize parameters into output speech */
    Synthesis (codec_dec_par, codec_dec_frame, codec_dec_out);
    /* Float to integer */
    pf = codec_dec_out;
    for (x=0; x<F_LEN; x++)
    {
      f = *pf++;
      if (f > 32767)
        f = 32767;
      else if (f < -32768)
        f = -32768;
      *audio++ = (signed short)f;
    }
    codec_dec_frame++;
    d -= (CELP_BITS>>3);
    a -= F_LEN;
  }
  *dlen = *dlen - d;
  *alen = *alen - a;
  return 0;
}


/*** EOF ***/
